//
//  DbgNetClient.m
//  BioBreak
//
//  Created by terry franguiadakis on 3/5/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "DbgNet.h"

#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <unistd.h>

#if DEBUG

@implementation DbgNet


@synthesize delegate;

- (void) handleBindError
{	
	switch( errno )
	{
		case EACCES :
            NSLog(@"For sockets in the AF_UNIX address family, the specified address does not specify a disk file local to the machine.");
            NSLog(@"	For sockets in the AF_UNIX address family, a component of the path prefix denies search permission, or the requested name requires writing in a directory with a mode that denies write permission.");
            break;
            
        case EADDRINUSE:
            NSLog(@"The specified address is already in use. [See the SO_REUSEADDR socket option under setsockopt().]");
            break;
        case EAFNOSUPPORT: 
            NSLog(@"The protocol used in the creation of specified socket does not support the specified address family.");
            break;
        case EBADF :
           NSLog(@"s is not a valid descriptor.");
            break;
        case EFAULT :
            NSLog(@"name is an invalid pointer or the namelen parameter is too small (less than the size of a struct sockaddr).");
            break;
        case EINVAL :
            NSLog(@"The socket is already bound to an address.");
            break;
        case ENETDOWN :
            NSLog(@"The Windows sockets implementation has detected that the network subsystem has failed.");
            break;
        case ENOBUFS :
            NSLog(@"Not enough buffers are available; there are too many connections.");
            break;
        case ENOTSOCK :
            NSLog(@"The descriptor is not a socket.");
            break;
	}
}


-(id)startListener 
{
    // create connection & start thread
    uint16_t chosenPort;
    
	if (m_socket.fd)
	{
		close(m_socket.fd);
		m_socket.fd = 0;
		free(m_socket.address);
	}
	
    if(m_socket.fd == 0) 
    {       
        // Here, create the socket from traditional BSD socket calls, and then set up an NSFileHandle with
        //that to listen for incoming connections.
        int fdForListening;
        struct sockaddr_in serverAddress;
        socklen_t namelen = sizeof(serverAddress);
		m_socket.addrLen = namelen;
        m_socket.address = new sockaddr_in;
		
		// create a listening socket.
        if((fdForListening = socket(AF_INET, SOCK_STREAM, 0)) > 0) 
        {
            memset(m_socket.address, 0, sizeof(struct sockaddr_in));
            m_socket.address->sin_family = AF_INET;
            m_socket.address->sin_addr.s_addr = htonl(INADDR_ANY);
            m_socket.address->sin_port = htons(8881);
			
            int optval = 1;
            
            setsockopt(fdForListening, AF_INET, SO_REUSEADDR, &optval, sizeof(optval));
            
            // Allow the kernel to choose a random port number by passing in 0 for the port.
            if (bind(fdForListening, (struct sockaddr *)m_socket.address, namelen) < 0) 
            {
				[self handleBindError];
                close (fdForListening);
				delete m_socket.address;
                return self;
            }
            
            // Find out what port number was chosen.
            if (getsockname(fdForListening, (struct sockaddr *)m_socket.address, &namelen) < 0) 
            {
                close(fdForListening);
				delete m_socket.address;
                return self;
            }
            
            chosenPort = ntohs(m_socket.address->sin_port);
            
            // Once we're here, we know bind must have returned, so we can start the listen
            if(listen(fdForListening, 1) == 0) 
            {
				m_socket.fd = fdForListening;
                // start listening
				[self performSelectorInBackground:@selector(listener) withObject:nil];

            }
        }
    }
    
    return self;
}

-(void)stopListener
{
    // close 
    if (m_socket.fd)
    {    
        close(m_socket.fd);
        delete m_socket.address;
    }
}

-(void)listener
{
    bool DoContinue = true;
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    
	while (DoContinue)
	{
		int connectSocket = accept(m_socket.fd, (struct sockaddr *)m_socket.address, &m_socket.addrLen);
		
		if (connectSocket != -1)
		{
			// we're connected
			
			bool ListeningToSocket = true;
            const int bufferSize = 1024;
			char dataBuffer[bufferSize];
			
			while(ListeningToSocket && DoContinue)
			{
				int receivedSize = recv(connectSocket, dataBuffer, bufferSize, 0);

				if (receivedSize == 0)
				{
					// we've lost connection
                     // Error( " lost connection " )
                    ListeningToSocket = false;
					break;
				}
				
				if (receivedSize == -1)
				{
                    // do something here to indicate error
                    // Error(" error connecting ")
                    ListeningToSocket = false;
					break;
				}
				
				// we have some data 
                @synchronized(delegate)
				{
                    [delegate NNCommand:dataBuffer];
                    memset(dataBuffer, 0, bufferSize);
				}
				
				// we don't really need to send a response
                static const char *ackbuffer = "ack";
				send(connectSocket,ackbuffer, 4, 0); 
			}
            
			close(connectSocket);
		}
		else
		{
			// error no socket...
			DoContinue = false;
		}
	}
    
    [pool release];    
}

@end

#endif //DEBUG
